#include "bsp.h"
#include "exception.h"
#include "engine.h"
#include "error.h"
#include "..\physics\physics.h"
#include "..\common\util.h"
#include "..\common\arraylist.hpp"
#include "..\common\types.h"
#include "..\common\format.h"
#include "..\render\d3d.h"
#include "..\render\d3d_mesh.h"
#include "..\physics\actor.h"
#include "..\math\matrix.h"
#include "..\render\interface_types.h"
#include "..\engine\engine.h"
#include "..\common\def.h"
#include "..\render\d3d.h"
#include "..\sound\dsound.h"
#include "..\input\dinput.h"

#ifdef _DEBUG_SHOWBSPSTATS
uint g_totalNodesVisited = 0;
uint g_totalLeavesDrawn = 0;
uint g_totalObjectsDrawn = 0;
#endif

void CBSP::Leaf::Draw(ViewOrrientation* view)
{
	for(uint i = 0, n = objects.Count(); i < n; i++) {
		objects[i]->Draw();
#ifdef _DEBUG_SHOWBSPSTATS
		g_totalObjectsDrawn++;
#endif
	}
	m_pMesh->Draw(Matrix4f::identity(), Vector3f());
#ifdef _DEBUG_SHOWBSPSTATS
	g_totalNodesVisited++;
	g_totalLeavesDrawn++;
#endif
}

bool CBSP::Leaf::IsVisible(ViewOrrientation* view)
{
	return true;
}

void CBSP::Leaf::InsertObject(CObject* pObject)
{
	objects.Add(pObject);
}

void CBSP::Node::Draw(ViewOrrientation* view)
{
#ifdef _DEBUG_SHOWBSPSTATS
	g_totalNodesVisited++;
#endif

	for(uint i = 0; i < objects.Count(); i++) {
		objects[i]->Draw();
#ifdef _DEBUG_SHOWBSPSTATS
		g_totalObjectsDrawn++;
#endif
	}
	
	if(Planef::ClassifyPoint(&m_splitPlane, &view->m_Position) == Planef::BELOW) {
		m_pLeftChild->Draw(view);
		if(m_pRightChild->IsVisible(view))
			m_pRightChild->Draw(view);
	}
	else {
		m_pRightChild->Draw(view);
		if(m_pLeftChild->IsVisible(view))
			m_pLeftChild->Draw(view);
	}
}

bool CBSP::Node::IsVisible(ViewOrrientation* view)
{
	return ViewOrrientation::ContainsSphere(view, &m_BoundSphere);// CEngine::GetInstance()->GetRenderer()->BoundSphereInFrustum(view, &m_BoundSphere);
}

void CBSP::Node::InsertObject(CObject* pObject)
{
	const float objectRadius = pObject->getModel()->GetBounds().m_radius;
	Vector3f objectPosition = pObject->getPosition();

	switch ( Planef::ClassifySphere(&m_splitPlane, &objectPosition, objectRadius) )
	{
	case Planef::BELOW: 
		{
			m_pRightChild->InsertObject(pObject);
		}break;
	case Planef::ABOVE: 
		{
			m_pLeftChild->InsertObject(pObject);
		}break;
	case Planef::INTERSECTING: 
		{
			objects.Add(pObject);
		}break;
	}
}

CBSP::CBSP(void) : CObject()			
{ 
	m_pRoot = NULL;
#ifdef _DEBUG_SHOWBSPSTATS
	g_totalNodesVisited = 0;
	g_totalLeavesDrawn = 0;
	g_totalObjectsDrawn = 0;
#endif
}	

CBSP::~CBSP(void)	
{ 

}

void CBSP::InsertRootObject(CObject* pObject)
{
	if(ISNULL(pObject) || ISNULL(m_pRoot))
	{
		EE_LOGERROR("Bad parameters.");
		return;
	}

	m_pRoot->objects.Add(pObject);
}

void CBSP::InsertObject(CObject* pObject)
{
	if(ISNULL(pObject) || ISNULL(m_pRoot))
	{
		EE_LOGERROR("Bad parameters.");
		return;
	}

	m_pRoot->InsertObject(pObject);

}

void CBSP::Draw(ViewOrrientation* pView)
{
	m_pRoot->Draw(pView);
}

void CBSP::AttachNode(const NodeData& innerNode)
{
	CBSP::Node* pNewNode = EE_NEW CBSP::Node();
	pNewNode->id = innerNode.id[NODE_ID];
	pNewNode->m_splitPlane = innerNode.splitPlane;
	pNewNode->m_BoundSphere = innerNode.boundSphere;

	m_Nodes.Add(ConstructDatum(pNewNode, innerNode.id[LEFTCHILD_ID],innerNode.id[RIGHTCHILD_ID]));

	if(ISNULL(m_pRoot))
	{
		m_pRoot = pNewNode;
	}
	else
	{
		for(uint i = 0; i < m_Nodes.Count(); i++)
		{
			ConstructDatum* pParentNode = &m_Nodes[i];
			if(pParentNode->m_leftChildID == pNewNode->id) {
				pParentNode->m_pNode->m_pLeftChild = pNewNode;
				break;
			}
			else if(pParentNode->m_rightChildID == pNewNode->id) {
				pParentNode->m_pNode->m_pRightChild = pNewNode;
				break;
			}
		}
	}
}

void CBSP::AttachLeaf(const LeafData& leaf)
{
	CBSP::Leaf* pNewLeaf = EE_NEW CBSP::Leaf();
	pNewLeaf->id = leaf.id;
	pNewLeaf->m_pMesh = leaf.pModel;

	m_Leaves.Add(pNewLeaf);

	if(ISNULL(m_pRoot))
	{
		m_pRoot = pNewLeaf;
	}
	else
	{
		for(uint i = 0; i < m_Nodes.Count(); i++)
		{
			ConstructDatum* pParentNode = &m_Nodes[i];
			if(pParentNode->m_leftChildID == pNewLeaf->id) {
				pParentNode->m_pNode->m_pLeftChild = pNewLeaf;
				break;
			}
			else if(pParentNode->m_rightChildID == pNewLeaf->id) {
				pParentNode->m_pNode->m_pRightChild = pNewLeaf;
				break;
			}
		}
	}

}